﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using SoundEdge.Media.Wave;
using SoundEdge.Media;

namespace SoundEdge.WinControls
{
    public class WaveViewer : Control, IWaveViewer
    {
        private Pen _foregroundPen;
        private Bitmap _surface;

        //List<float> samples = new List<float>(1000);
        //int maxSamples;
        //int insertPos;

        /// <summary>
        /// Constructs a new instance of the WaveFormPainter class
        /// </summary>
        public WaveViewer()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
                ControlStyles.OptimizedDoubleBuffer, true);
            OnForeColorChanged(EventArgs.Empty);
            OnResize(EventArgs.Empty);
        }

        /// <summary>
        /// On Resize
        /// </summary>
        protected override void OnResize(EventArgs e)
        {
            //maxSamples = this.Width;
            base.OnResize(e);
        }

        /// <summary>
        /// On ForeColor Changed
        /// </summary>
        /// <param name="e"></param>
        protected override void OnForeColorChanged(EventArgs e)
        {
            _foregroundPen = new Pen(ForeColor);
            base.OnForeColorChanged(e);
        }

        ///// <summary>
        ///// Add Max Value
        ///// </summary>
        ///// <param name="maxSample"></param>
        //public void AddMax(float maxSample)
        //{
        //    if (maxSamples == 0)
        //    {
        //        // sometimes when you minimise, max samples can be set to 0
        //        return;
        //    }
        //    if (samples.Count <= maxSamples)
        //    {
        //        samples.Add(maxSample);
        //    }
        //    else
        //    {
        //        samples[insertPos] = maxSample;
        //    }
        //    insertPos++;
        //    insertPos %= maxSamples;

        //    this.Invalidate();
        //}

        /// <summary>
        /// On Paint
        /// </summary>
        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);

            if (Provider == null || _surface == null)
            {
                return;
            }

            pe.Graphics.DrawImageUnscaled(_surface, 0, 0);
        }

        //float GetSample(byte channelNo, int index)
        //{
        //    if (index < 0)
        //        index += maxSamples;
        //    if (index >= 0 & index < samples.Count)
        //        return samples[index];
        //    return 0;
        //}

        private IWaveProvider _provider;
        List<List<float>> _buffer;
        public IWaveProvider Provider
        {
            get { return _provider; }
            set { _provider = value; SetBuffer(); }
        }

        private void SetBuffer()
        {
            if (Provider == null)
            {
                return;
            }
            int bufferSize = Provider.WaveFormat.ConvertLatencyToByteSize(100);
            byte[] buffer = new byte[bufferSize];
            int samplesRead = Provider.Read(buffer, 0, bufferSize);
            int channels = Provider.WaveFormat.Channels;

            List<List<float>> channelBuffers = new List<List<float>>();

            for (int index = 0; index < samplesRead; index += channels)
            {
                for (int channel = 0; channel < channels; channel++)
                {
                    if (channelBuffers.Count - 1 < channel)
                    {
                        channelBuffers.Add(new List<float>());
                    }
                    float sampleValue = Math.Abs(buffer[index + channel]);
                    channelBuffers[channel].Add(sampleValue);//Math.Max(streamValues[channel], sampleValue);
                }
            }
            _buffer = channelBuffers;
            DrawRawSurface();
            Invalidate();
        }

        public float[] FloatArrayFromByteArray(byte[] input)
        {
            float[] output = new float[input.Length / 4];
            Buffer.BlockCopy(input, 0, output, 0, input.Length);
            return output;
        }

        private void DrawRawSurface()
        {
            if (_buffer == null || _buffer.Count == 0)
            {
                return;
            }

            int maxHeight = GetMaxBufferHeight();
            _surface = new Bitmap(maxHeight, this.Height);
            List<Point> markers = new List<Point>();
            for (int x = 0; x < _buffer[0].Count; x++)
            {
                markers.Add(new Point(x, (int)_buffer[0][x]));
            }
            using (Graphics g = Graphics.FromImage(_surface))
            {
                g.DrawLines(_foregroundPen, markers.ToArray());
            }
        }

        private int GetMaxBufferHeight()
        {
            if (_buffer == null)
            {
                return 0;
            }
            int max = 0;
            foreach (List<float> channelBuffer in _buffer)
            {
                for (int idx = 0; idx < channelBuffer.Count; idx++)
                {
                    if (max < channelBuffer[idx])
                    {
                        max = (int)channelBuffer[idx];
                    }
                }
            }
            return max;
        }

        public void Draw()
        {
            if (Provider == null)
            {
                return;
            }
            Invalidate();
        }
    }
}
